///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_APP_CCMDLINEOPTION_H_
#define _BUOLA_APP_CCMDLINEOPTION_H_

#include <buola/buola.h>
#include <vector>

namespace buola {

///\addtogroup application
///@{

namespace detail {
    
std::vector<CCmdLineFlag*> &all_cmd_line_options();
    
/*namespace detail*/ }

/////////////////////////////////////////////////////////////////////////////////////////////////

/// command line flag

/// Objects of this class represent possible command-line flags for the application. It is 
/// also the base class for command-line options which accept a value.
///
/// When the object is created, it gets registered as a possible command-line option. Then,
/// this class also provides options for parsing all the options into a CCmdLine object. Finally,
/// the CCmdLineFlag object can also be used to test the CCmdLine for presence of this flag or
/// option.
///
/////////////////////////////////////////////////////////////////////////////////////////////////

class CCmdLineFlag
{
public:
/// \name Constructor and destructor
//@{
    /// creates an option which has long and short versions

    /// \param pLong long option name (can be specified as --long-option)
    /// \param pShort short option name (can be used as -s)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineFlag(const std::string &pLong,char pShort,const std::wstring &pHelp)
        :   mLong(pLong),
            mShort(pShort),
            mHelp(pHelp)
    {
        detail::all_cmd_line_options().push_back(this);
    }

    /// creates a long option

    /// \param pLong long option name (can be specified as --long-option)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineFlag(const std::string &pLong,const std::wstring &pHelp)
        :   mLong(pLong),
            mShort('\0'),
            mHelp(pHelp)
    {
        detail::all_cmd_line_options().push_back(this);
    }

    /// creates a short option

    /// \param pShort short option name (can be used as -s)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineFlag(char pShort,const std::wstring &pHelp)
        :   mShort(pShort),
            mHelp(pHelp)
    {
        detail::all_cmd_line_options().push_back(this);
    }

    virtual ~CCmdLineFlag() {}
    
    explicit operator bool() const;
    
//@}

private:
    /// for parsing of options which accept parameters
    virtual void* ParseParameter(const std::string&) const
    {
        throw XOptionParsing("unexpected option parameter");
    }

    /// check if the option accepts a parameter
    virtual bool HasParameter() const
    {
        return false;
    }
    
    virtual bool Required() const
    {
        return false;
    }

    std::string mLong;
    char mShort;
    std::wstring mHelp;
    
    friend class CCmdLine;
};

///////////////////////////////////////////////////////////////////////////

/// command line option (which accepts a parameter)

/// Objects of this class represent possible command-line options which
/// accept a parameter of the given type (as a template argument).
///
/// If it is a string type, the given parameter is returned as-is.
/// Otherwise, it is converted via to_<>.
///
////////////////////////////////////////////////////////////////////////////

template<typename tType>
class CCmdLineOption : public CCmdLineFlag
{
public:
/// \name Constructor and destructor
//@{
    /// creates an option which has long and short versions

    /// \param pLong long option name (can be specified as --long-option)
    /// \param pShort short option name (can be used as -s)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineOption(const std::string &pLong,char pShort,const std::wstring &pHelp,const tType &pDefault=tType())
        :   CCmdLineFlag(pLong,pShort,pHelp)
        ,   mDefault(pDefault)
        ,   mRequired(false)
    {}

    /// creates an option which has long and short versions

    /// \param pLong long option name (can be specified as --long-option)
    /// \param pShort short option name (can be used as -s)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineOption(const std::string &pLong,char pShort,const std::wstring &pHelp,NRequired)
        :   CCmdLineFlag(pLong,pShort,pHelp)
        ,   mRequired(true)
    {}

    /// creates a long option

    /// \param pLong long option name (can be specified as --long-option)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineOption(const std::string &pLong,const std::wstring &pHelp,const tType &pDefault=tType())
        :   CCmdLineFlag(pLong,pHelp)
        ,   mDefault(pDefault)
        ,   mRequired(false)
    {}

    /// creates a long option

    /// \param pLong long option name (can be specified as --long-option)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineOption(const std::string &pLong,const std::wstring &pHelp,NRequired)
        :   CCmdLineFlag(pLong,pHelp)
        ,   mRequired(true)
    {}

    /// creates a short option

    /// \param pShort short option name (can be used as -s)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineOption(char pShort,const std::wstring &pHelp,const tType &pDefault=tType())
        :   CCmdLineFlag(pShort,pHelp)
        ,   mDefault(pDefault)
        ,   mRequired(false)
    {}

    /// creates a short option

    /// \param pShort short option name (can be used as -s)
    /// \param pHelp text to be displayed as help for the option
    CCmdLineOption(char pShort,const std::wstring &pHelp,NRequired)
        :   CCmdLineFlag(pShort,pHelp)
        ,   mRequired(true)
    {}

    const tType &GetDefault() const  {   return mDefault;    }

    const tType* operator->() const;
    const tType& operator*() const;

private:
    /// parses the parameter to the option
    virtual void* ParseParameter(const std::string &pText) const
    {
        return new tType(to_<tType>(pText));
    }

    /// returns true
    virtual bool HasParameter() const
    {
        return true;
    }
    
    ///returns true
    virtual bool Required() const
    {
        return mRequired;
    }
    
private:
    tType mDefault;
    bool mRequired;
};

///@}

/*namespace buola*/ }

#endif
